package com.ztom.beans.factory

import com.ztom.beans.exception.BeansException
import com.ztom.beans.factory.support.pojo.BeanDefinition
import com.ztom.beans.factory.support.pojo.FactoryBean
import com.ztom.beans.factory.support.processor.BeanPostProcessor
import com.ztom.beans.factory.support.registry.DefaultSingletonBeanRegistry

/**
 * 抽象 getBean 方法
 * 提供模板方法 createBean 和 getBeanDefinition
 *
 * @author ZhangTao
 */
abstract class AbstractBeanFactory : DefaultSingletonBeanRegistry(), ConfigurableBeanFactory {

    private val beanPostProcessorList = ArrayList<BeanPostProcessor>()

    private val factoryBeanCache = HashMap<String, Any>()

    override fun getBean(beanName: String): Any {
//        return getSingleton(beanName) ?: createBean(beanName, getBeanDefinition(beanName))
        val sharedInstance = getSingleton(beanName)
        return if (sharedInstance != null) {
            return getObjectFromBeanInstance(sharedInstance, beanName)
        } else {
            val bean = createBean(beanName, getBeanDefinition(beanName))
            return getObjectFromBeanInstance(bean, beanName)
        }
    }

    private fun getObjectFromBeanInstance(beanInstance: Any, beanName: String): Any {
        var bean = beanInstance
        if (beanInstance !is FactoryBean<*>) {
            return bean
        }
        val factoryBean = bean as FactoryBean<*>
        try {
            bean = if (factoryBean.isSingleton()) {
                // 单例 bean 维护缓存
                factoryBeanCache.computeIfAbsent(beanName) {
                    factoryBean.getObject()!!
                }
            } else {
                // 原型 bean 每次创建
                factoryBean.getObject()!!
            }
        } catch (e: Exception) {
            throw BeansException("Factory bean threw an exception on object [$beanName] creation", e)
        }
        return bean
    }

    protected abstract fun createBean(beanName: String, beanDefinition: BeanDefinition): Any

    protected abstract fun getBeanDefinition(beanName: String): BeanDefinition

    override fun addBeanPostProcessor(beanPostProcessor: BeanPostProcessor) {
        beanPostProcessorList -= beanPostProcessor
        beanPostProcessorList += beanPostProcessor
    }

    fun getBeanPostProcessorList(): ArrayList<BeanPostProcessor> {
        return beanPostProcessorList
    }
}